package com.activenetwork.custom;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.FindBys;

import com.activenetwork.custom.uielement.decorate.InJson;

public class PageFactoryUtil {
	
	/**
	 * Transfer the List<WebElement> to List<T> which T is the sub interface of WebElement
	 * @param value
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Object createCastValueList(Object value, Class<? extends WebElement> clazz){
		List<WebElement> elements = (List<WebElement>)value;
		List<WebElement> castElements = new ArrayList<WebElement>();
		for(WebElement element : elements){
			try {
				Constructor<? extends WebElement> con = clazz.getDeclaredConstructor(new Class[]{WebElement.class});
				con.setAccessible(true);
				WebElement caseElement = con.newInstance(element);
				castElements.add(caseElement);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return castElements;
	}
	
	/**
	 * Calling constructor to initialize page instace
	 * @param <T>
	 * @param driver
	 * @param pageClassToProxy
	 * @return
	 */
	public static <T> T instantiatePage(WebDriver driver, Class<T> pageClassToProxy) {
		try {
			try {
				Constructor<T> constructor = pageClassToProxy.getConstructor(WebDriver.class);
				return constructor.newInstance(driver);
			} catch (NoSuchMethodException e) {
				return pageClassToProxy.newInstance();
			}
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * Check if the field is List<WebElement> or List<T> which T is the sub
	 * interface of WebElement
	 * 
	 * @param field
	 * @return
	 */
	public static boolean isDecoratableList(Field field) {
		if (!List.class.isAssignableFrom(field.getType())) {
			return false;
		}

		// Type erasure in Java isn't complete. Attempt to discover the generic
		// type of the list.
		Type genericType = field.getGenericType();
		if (!(genericType instanceof ParameterizedType)) {
			return false;
		}

		// if interface A extends WebElement
		// if field is List<A> then we get listType is A, but this is not enough
		// genericListType is the super interface of A, that is WebElement
		Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0];
		Type genericListType = ((Class<?>) listType).getGenericInterfaces()[0];

		// if field is not:
		// List<WebElement>
		// List<T> which T is the sub interface of WebElement
		// then false
		if (!WebElement.class.equals(listType) && !WebElement.class.equals(genericListType)) {
			return false;
		}

		// if the field has not these annotations
		if (field.getAnnotation(FindBy.class) == null
				&& field.getAnnotation(FindBys.class) == null
				&& field.getAnnotation(InJson.class) == null) {
			return false;
		}

		return true;
	}

}
